Import cal3d

Class CalAbstractMixer
	
		
	Method New()
	
	End
	
	Method New(model:CalModel)
	
	End
	
	Method isDefaultMixer:Bool()
		Return False
	End	
	
	Method updateAnimation:Void(deltaTime:Float)
		Print "This is the abstract mixer! Use one of the extended classes!"
	End
	
	Method updateSkeleton:Void()
		Print "This is the abstract mixer! Use one of the extended classes!"
	End
	
	
End

Class CalMixer Extends CalAbstractMixer
	
	Field m_model:CalModel
	Field m_vectorAnimation:CalAnimation[]
	Field m_listAnimationAction:CalAnimationAction[]
	Field m_listAnimationCycle:CalAnimationCycle[]
	Field m_animationTime:Float
	Field m_animationDuration:Float
	Field m_timeFactor:Float
	
	Method New(model:CalModel)
		m_model = model
		m_vectorAnimation = New CalAnimation[model.getCoreModel().getCoreAnimationCount()]
		m_listAnimationAction = New CalAnimationAction[0]
		m_listAnimationCycle = New CalAnimationCycle[0]
		m_animationTime = 0
		m_animationDuration = 0
		m_timeFactor = 1
		
		
	End
	
	Method isDefaultMixer:Bool()
		Return True
	End
	
	Method blendCycle:Bool(id:Int, weight:Float, delay:Float)
	
		If id < 0 Or id >= m_vectorAnimation.Length Then
			Print "Invalid Animation ID"
			Return False
		Endif
	
		' get the animation for the given id
		Local animation:CalAnimation = m_vectorAnimation[id]
	
		' create a new animation instance if it is not active yet
		If animation = Null then
			' take the fast way out if we are trying to clear an inactive animation
			If weight = 0 Then
				Return True
			Endif
			' get the core animation
			Local coreAnimation:CalCoreAnimation = m_model.getCoreModel().getCoreAnimation(id)
			If coreAnimation = Null Then
				Return False
			Endif
			' ensure that the animation's first and last key frame match for proper looping
			addExtraKeyframeForLoopedAnim(coreAnimation)
	
			' create a new animation cycle instance
			Local animationCycle:CalAnimationCycle = New CalAnimationCycle(coreAnimation)
	
			' insert the new animation into the tables
			m_vectorAnimation[id] = animationCycle
			Local lac:CalAnimationCycle[] = New CalAnimationCycle[m_listAnimationCycle.Length+1]
			For Local i:Int = 1 To m_listAnimationCycle.Length
				lac[i] = m_listAnimationCycle[i-1]
			Next
			lac[0] = animationCycle
			m_listAnimationCycle = lac
	
			' blend the animation
			Return animationCycle.blend(weight, delay)
		Endif
	
		' check if this is really an animation cycle instance
		If animation.getType() <> CalAnimationType.TYPE_CYCLE Then
			Print "Not Animation Cycle Class"
			Return False
		Endif
	
		' clear the animation cycle from the active vector if the target weight is zero
		If weight = 0 Then
			m_vectorAnimation[id] = Null
		Endif
	
		' cast it to an animation cycle
		Local animationCycle:CalAnimationCycle = CalAnimationCycle(animation)
	
		' blend the animation cycle
		animationCycle.blend(weight, delay)
		animationCycle.checkCallbacks(0, m_model)
	
		Return True		
		
	End
	
	Method clearCycle:Bool(id:Int, delay:Float)
		' check if the animation id is valid
		If id < 0 Or id >= m_vectorAnimation.Length then
			Print "Invalid Animation ID"
			Return False
		Endif
	
		' get the animation for the given id
		Local animation:CalAnimation = m_vectorAnimation[id]
	
		' we can only clear cycles that are active
		If animation = Null Then
			Return True
		Endif
		' check if this is really a animation cycle instance
		If animation.getType() <> CalAnimationType.TYPE_CYCLE then
			Print "Invalid Animation Type"
			Return False
		Endif
	
		' clear the animation cycle from the active vector
		m_vectorAnimation[id] = Null
	
		' cast it to an animation cycle
		Local animationCycle:CalAnimationCycle = CalAnimationCycle(animation)
	
		' set animation cycle to async state
		animationCycle.setAsync(m_animationTime, m_animationDuration)
	
		' blend the animation cycle
		animationCycle.blend(0, delay)
		animationCycle.checkCallbacks(0, m_model)
	
		Return True	
	
	End
	
	
	Method executeAction:Bool(id:Int, delayIn:Float, delayOut:Float, weightTarget:Float, autoLock:Bool)
		
		' get the core animation
		Local coreAnimation:CalCoreAnimation = m_model.getCoreModel().getCoreAnimation(id)
		
		If coreAnimation = Null Then
			Return False
		Endif
		
		' create a new animation action instance
		Local animationAction:CalAnimationAction = New CalAnimationAction(coreAnimation)
	
		' insert the new animation into the table
		Local laa:CalAnimationAction[] = New CalAnimationAction[m_listAnimationAction.Length+1]
		
		For Local i:Int = 1 To m_listAnimationAction.Length
			laa[i] = m_listAnimationAction[i-1]
		Next
		laa[0] = animationAction
		m_listAnimationAction = laa
	
		' execute the animation
		animationAction.execute(delayIn, delayOut, weightTarget, autoLock)
		animationAction.checkCallbacks(0, m_model)
	
		Return True		
		
	End
	
	Method removeAction:Bool(id:Int)

		' get the core animation
		Local coreAnimation:CalCoreAnimation = m_model.getCoreModel().getCoreAnimation(id)
		If coreAnimation = Null Then
			Return False
		Endif
		
		' update all active animation actions of this model
		Local animationActionId:Int = 0
		While animationActionId < m_listAnimationAction.Length
			' find the specified action and remove it
			If m_listAnimationAction[animationActionId].getCoreAnimation() = coreAnimation Then
				' found, so remove
				m_listAnimationAction[animationActionId].completeCallbacks(m_model)
				Local laa:CalAnimationAction[] = New CalAnimationAction[m_listAnimationAction.Length-1]
				Local pos:Int=0
				For Local i:Int = 0 To m_listAnimationAction.Length-1
					If i <> animationActionId Then
						laa[pos] = m_listAnimationAction[i]
						pos=pos+1
					Endif
				Next
				m_listAnimationAction = laa
				'm_listAnimationAction.splice(animationActionId, 1)
				Return True
			Endif
	
			animationActionId=animationActionId+1
		Wend
	
		Return False		

	End
	
	
	Method updateAnimation:Void(deltaTime:Float)
	
	
		' update the current animation time
		If m_animationDuration = 0 Then
			m_animationTime = 0		
		Else
			m_animationTime += deltaTime * m_timeFactor
			
			If m_animationTime >= m_animationDuration Or m_animationTime < 0 Then
				m_animationTime = Modf(m_animationTime,m_animationDuration)
			Endif
			
			If m_animationTime < 0 Then
				m_animationTime = m_animationTime + m_animationDuration
			Endif
		Endif
	
		' update all active animation actions of this model
		Local animationActionId:Int = 0
		
		While animationActionId < m_listAnimationAction.Length
		
			Local animationAction:CalAnimationAction = m_listAnimationAction[animationActionId]
			' update and check if animation action is still active
			If animationAction.update(deltaTime) Then
				animationAction.checkCallbacks(animationAction.getTime(), m_model)
				animationActionId = animationActionId + 1
			Else
				' animation action has ended, destroy and remove it from the animation list
				animationAction.completeCallbacks(m_model)
				Local laa:CalAnimationAction[] = New CalAnimationAction[m_listAnimationAction.Length-1]
				Local pos:Int = 0
				For Local i:Int = 0 To m_listAnimationAction.Length-1
					If i <> animationActionId Then
						laa[pos] = m_listAnimationAction[i]
						pos = pos + 1
					Endif
				Next
				m_listAnimationAction = laa
			Endif
		Wend
	
		' TODO: update all active animation poses of this model
		' ...
	
		' update the weight of all active animation cycles of this model
		Local accumulatedWeight:Float = 0
		Local accumulatedDuration:Float = 0
		Local animationCycleId:Int = 0
		While animationCycleId < m_listAnimationCycle.Length
			Local animationCycle:CalAnimationCycle = m_listAnimationCycle[animationCycleId]
			' update and check if animation cycle is still active
			If animationCycle.update(deltaTime) = True Then
				' check if it is in sync. if yes, update accumulated weight and duration
				If animationCycle.getState() = CalAnimationState.STATE_SYNC Then
					accumulatedWeight += animationCycle.getWeight()
					accumulatedDuration += animationCycle.getWeight() * animationCycle.getCoreAnimation().getDuration()
				Endif
	
				animationCycle.checkCallbacks(m_animationTime, m_model)
				animationCycleId=animationCycleId+1
			
			Else
				' animation cycle has ended, destroy and remove it from the animation list
				animationCycle.completeCallbacks(m_model)
				Local lac:CalAnimationCycle[] = New CalAnimationCycle[m_listAnimationCycle.Length-1]
				Local pos:Int = 0
				For Local i:Int = 0 To m_listAnimationCycle.Length-1
					If i <> animationCycleId Then
						lac[pos] = m_listAnimationCycle[i]
						pos=pos+1
					Endif
				Next
				m_listAnimationCycle = lac
			Endif
		Wend
	
		' adjust the global animation cycle duration
		If accumulatedWeight > 0 Then
			m_animationDuration = accumulatedDuration / accumulatedWeight
		Else
			m_animationDuration = 0
		Endif
	
	
	End
	
	
	Method updateSkeleton:Void()
		' get the skeleton we need to update
		Local skeleton:CalSkeleton = m_model.getSkeleton()
		If skeleton = Null Then 
			Return
		Endif
		' clear the skeleton state
		skeleton.clearState()
	
		' get the bone vector of the skeleton
		Local vectorBone:CalBone[] = skeleton.getVectorBone()
	
		Local translation:CalVector = New CalVector
		Local rotation:CalQuaternion = New CalQuaternion
	
		' loop through all animation actions
		For Local animationActionId:Int = 0 To m_listAnimationAction.Length-1
			Local animationAction:CalAnimationAction = m_listAnimationAction[animationActionId]
			
			' get the core animation instance
			Local coreAnimation:CalCoreAnimation = animationAction.getCoreAnimation()
	
			' get the list of core tracks of above core animation
			Local listCoreTrack:CalCoreTrack[] = coreAnimation.getListCoreTrack()
	
			' loop through all core tracks of the core animation
			For Local coreTrackId:Int = 0 To listCoreTrack.Length-1
				' get the appropriate bone of the track
				Local bone:CalBone = vectorBone[ listCoreTrack[coreTrackId].getCoreBoneId() ]
	
				' get the current translation and rotation
				listCoreTrack[coreTrackId].getState(animationAction.getTime(), translation, rotation)
	
				' blend the bone state with the new state
				bone.blendState(animationAction.getWeight(), translation, rotation)
			Next
		Next
	
		' lock the skeleton state
		skeleton.lockState()
	
		' loop through all animation cycles
		For Local animationCycleId:Int = 0 To m_listAnimationCycle.Length-1
			Local animationCycle:CalAnimationCycle = m_listAnimationCycle[animationCycleId]
	
			' get the core animation instance
			Local coreAnimation:CalCoreAnimation = animationCycle.getCoreAnimation()
	
			' calculate adjusted time
			Local animationTime:Float
			If animationCycle.getState() = CalAnimationState.STATE_SYNC Then
				If m_animationDuration = 0 Then
					animationTime = 0
				Else
					animationTime = m_animationTime * coreAnimation.getDuration() / m_animationDuration
				Endif
			Else
				animationTime = animationCycle.getTime()
			Endif
			' get the list of core tracks of above core animation
			Local listCoreTrack:CalCoreTrack[] = coreAnimation.getListCoreTrack()
	
			' loop through all core tracks of the core animation
			For Local coreTrackId:Int = 0 To listCoreTrack.Length-1
				' get the appropriate bone of the track
				Local bone:CalBone = vectorBone[ listCoreTrack[coreTrackId].getCoreBoneId() ]
	
				' get the current translation and rotation
				listCoreTrack[coreTrackId].getState(animationTime, translation, rotation)
	
				' blend the bone state with the new state
				bone.blendState(animationCycle.getWeight(), translation, rotation)
			Next
		Next
	
		' lock the skeleton state
		skeleton.lockState()
	
		' let the skeleton calculate its final state
		skeleton.calculateState()		
	End
	
	
	Method getAnimationTime:Float()
		Return m_animationTime
	End
		
	Method getAnimationDuration:Float()
		Return m_animationDuration
	End	
	
	Method setAnimationTime:Void(animationTime:Float)
		m_animationTime = animationTime
	End
	
	Method setTimeFactor:Void(timeFactor:Float)
		m_timeFactor = timeFactor
	End
	
	Method getTimeFactor:Float()
		Return m_timeFactor
	End
	
	Method getCalModel:CalModel()
		Return m_model
	End
	
	Method getAnimationVector:CalAnimation[]()
		Return m_vectorAnimation
	End
	
	Method getAnimationActionList:CalAnimationAction[]()
		Return m_listAnimationAction
	End
	
	Method getAnimationCycle:CalAnimationCycle[]()
		Return m_listAnimationCycle
	End
	
	
		
End Class


Function addExtraKeyframeForLoopedAnim:Void(coreAnimation:CalCoreAnimation)

	Local listCoreTrack:CalCoreTrack[] = coreAnimation.getListCoreTrack()
	If listCoreTrack.Length = 0 Then
		Return
	Endif
	
	Local coreTrackId:Int = 0
	If listCoreTrack[coreTrackId] = Null Then
		Return
	Endif
	Local lastKeyframe:CalCoreKeyframe = listCoreTrack[coreTrackId].getCoreKeyframe( listCoreTrack[coreTrackId].getCoreKeyframeCount() - 1 )
	If lastKeyframe = Null Then
		Return
	Endif
	If lastKeyframe.getTime() < coreAnimation.getDuration() Then
		For coreTrackId = 0 To listCoreTrack.Length-1
			Local coreTrack:CalCoreTrack = listCoreTrack[coreTrackId]

			Local firstKeyframe:CalCoreKeyframe = coreTrack.getCoreKeyframe(0)
			Local newKeyframe:CalCoreKeyframe = New CalCoreKeyframe

			newKeyframe.setTranslation(firstKeyframe.getTranslation())
			newKeyframe.setRotation(firstKeyframe.getRotation())
			newKeyframe.setTime(coreAnimation.getDuration())

			coreTrack.addCoreKeyframe(newKeyframe)
		Next
	Endif	

End